#include <gl\glew.h>
#include <gl\freeglut.h>

#include <iostream> //adding this to be able to output debug information to the console

#pragma comment (lib, "glew32.lib")
#pragma comment (lib, "freeglut.lib")


//glob_al variables (each glob_al variable has the "glob_" prefix to optimize for IntelliSense - just write glob_ and pres Alt + Left Arrow and you'll see all glob_al variables)
bool glob_KeyStates[256]; //we have 256 character codes which we receive as information for the keyboard

int glob_WindowPositionX = 50,
	glob_WindowPositionY = 50,
	glob_WindowSizeX = 400,
	glob_WindowSizeY = 400;

double glob_AngleOfView_degs = 60,
	glob_NearPlaneDist = 1,
	glob_FarPlaneDist = 1000,
	glob_WorldUpVectorX = 0,
	glob_WorldUpVectorY = 1,
	glob_WorldUpVectorZ = 0;


//camera variables
double glob_CamPosX = 20,
	glob_CamPosY = 20,
	glob_CamPosZ = 20,
	glob_CamLookX = 0,
	glob_CamLookY = 0,
	glob_CamLookZ = 0;
//----------------

//teapot variables
double glob_TeapotRotY_degs = 0.0;

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void ReshapeWindow(int width, int height)
{
	glViewport(0, 0, width, height);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();

	double aspectRatio = ((float)width) / ((float)height);

	gluPerspective(glob_AngleOfView_degs, aspectRatio, glob_NearPlaneDist, glob_FarPlaneDist);
	glMatrixMode (GL_MODELVIEW);
}

void HandleKeyDown (unsigned char key, int cursorX, int cursorY)
{

	glob_KeyStates[key] = true;
}

void HandleKeyUp (unsigned char key, int cursorX, int cursorY)
{
	glob_KeyStates[key] = false;
}

void HandleKeyboard()
{
	if(glob_KeyStates['r'] || glob_KeyStates['R'])
	{
		glob_TeapotRotY_degs+=1.0;
	}
	if(glob_KeyStates['e'] || glob_KeyStates['E'])
	{
		glob_TeapotRotY_degs-=1.0;
	}
}

void ApplyCamera(double camPosX, double camPosY, double camPosZ, double camLookX, double camLookY, double camLookZ)
{
	gluLookAt(camPosX, camPosY, camPosZ, camLookX, camLookY, camLookZ, glob_WorldUpVectorX, glob_WorldUpVectorY, glob_WorldUpVectorZ);
}

void DrawCoordinateSystemZeroSphere()
{
	glPushMatrix();
	glutSolidSphere(1, 10, 10);
	glPopMatrix();
}

void DrawTeapot()
{
	int teapotSize = 5;
	double teapotLocationX = 10, //shiftint the teapot's center away from the center of the global coordinate system
		teapotLocationY = 0, 
		teapotLocationZ = 0;

	double teapotRotationX_degs = 0,
		teapotRotationY_degs = 0,
		teapotRotationZ_degs = 0;

	double teapotColorRed = 1.0,
		teapotColorGreen = 1.0,
		teapotColorBlue = 1.0,
		teapotColorAlpha = 1.0;

	teapotRotationY_degs = glob_TeapotRotY_degs;

	//drawing a teapot
	glPushMatrix(); //this saves our last orienatation, so we can get back to it later with glPopMatrix (we'll learn about this later)

	glTranslatef(teapotLocationX, teapotLocationY, teapotLocationZ);
	glRotatef(teapotRotationX_degs, 1.0, 0.0, 0.0);
	glRotatef(teapotRotationY_degs, 0.0, 1.0, 0.0);
	glRotatef(teapotRotationZ_degs, 0.0, 0.0, 1.0);

	glColor4f(teapotColorRed, teapotColorGreen, teapotColorBlue, teapotColorAlpha); // applying the color
	glutSolidTeapot(teapotSize);

	glPopMatrix(); //getting back to the old orientation
	//-----------------
}

void DrawObjects()
{
	DrawCoordinateSystemZeroSphere();
	DrawTeapot();
}

void Display()
{
	glClearColor(0.0, 0.0, 0.0, 1.0); //set to what color to clear the color buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear old information from the color and depth buffer
	glLoadIdentity(); //initialize the identity matrix (default the orientation)

	ApplyCamera(glob_CamPosX, glob_CamPosY, glob_CamPosZ, glob_CamLookX, glob_CamLookY, glob_CamLookZ);
	DrawObjects();

	glutSwapBuffers(); //send what we have drawn to the screen
}

void MainLoop()
{
	HandleKeyboard();
	Display();
}

void InitializeWindow()
{
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); //we need double buffering, depth buffering and a RGBA color scheme
	glutInitWindowPosition(glob_WindowPositionX, glob_WindowPositionY);
	glutInitWindowSize(glob_WindowSizeX, glob_WindowSizeY);

	glutCreateWindow("OpenGL Basics Full Demo 12.07.2011");
}

void InitializeGL()
{
	glEnable(GL_DEPTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
}

void RegisterFunctions()
{
	glutIdleFunc(MainLoop);
	glutDisplayFunc(Display);
	glutReshapeFunc(ReshapeWindow);
	glutKeyboardFunc(HandleKeyDown);
	glutKeyboardUpFunc(HandleKeyUp);
}

int main (int argc, char ** argv)
{
	glutInit(&argc, argv);
	InitializeWindow();
	InitializeGL();
	RegisterFunctions();

	glutMainLoop();
}